package com.bai.localshop.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.aliyun.oss.ServiceException;
import com.bai.localshop.context.BaseContext;
import com.bai.localshop.exception.BaseException;
import com.bai.localshop.mapper.VoucherMapper;
import com.bai.localshop.pojo.dto.*;
import com.bai.localshop.pojo.entity.Voucher;
import com.bai.localshop.pojo.vo.UserVoucherQueryVO;
import com.bai.localshop.pojo.vo.VoucherVO;
import com.bai.localshop.service.AuthService;
import com.bai.localshop.service.VoucherService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.bai.localshop.constant.RedisKeyConstant.*;
import static com.bai.localshop.constant.SystemConstants.*;

/**
 * 代金券服务实现类
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class VoucherServiceImpl extends ServiceImpl<VoucherMapper, Voucher> implements VoucherService {

    private final VoucherMapper voucherMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final RBloomFilter<String> createCachePenetrationBloomFilter;
    private final RedissonClient redissonClient;
    private final AuthService userService;

    /**
     * 分页查询代金券
     */
    @Override
    public <T> IPage<T> pageQuery(VoucherQueryDTO requestParam, Class<T> voClass) {
        //获取用户身份
        String roleName = userService.getUserById(BaseContext.getCurrentId()).getRoleName();
        //身份是普通用户
        //todo 抽取常量类
        if (!"管理员".equals(roleName)) {
            // 用户端固定条件：只查询进行中代金券
            requestParam.setStatus(1);
            // 管理员专用条件设置为空
            requestParam.setMinPayValue(null);
            requestParam.setMaxPayValue(null);
        }
        // 设置默认分页参数
        int page = requestParam.getPage() != null ? requestParam.getPage() : 1;
        int pageSize = requestParam.getPageSize() != null ? requestParam.getPageSize() : 10;

        // 执行分页查询
        IPage<Voucher> voucherIPage = voucherMapper.selectVoucherPage(
                new Page<>(page, pageSize),
                requestParam
        );
        // 动态转换为不同的VO
        return voucherIPage.convert(each -> BeanUtil.toBean(each, voClass));
    }

    /**
     * 根据id查询代金券
     */
    @Override
    public VoucherVO getById(Long id) {
        //查询缓存
        String voucherStr = stringRedisTemplate.opsForValue().get(String.format(VOUCHER_KEY, id));
        if (StrUtil.isNotBlank(voucherStr)) {
            return JSON.parseObject(voucherStr, VoucherVO.class);
        }
        //检查空值缓存
        if (StrUtil.isNotBlank(stringRedisTemplate.opsForValue().get(String.format(NULL_VOUCHER_KEY, id)))) {
            return null;
        }
        //检查布隆过滤器中是否有数据（防止缓存穿透）
        boolean contains = createCachePenetrationBloomFilter.contains(id.toString());
        if (!contains) {
            return null;
        }
        //获取分布式锁对象
        RLock lock = redissonClient.getLock("lock_voucher:" + id);
        //获取分布式锁
        try {
            boolean locked = lock.tryLock(10, 30, TimeUnit.SECONDS);
            if (!locked) {
                throw new RuntimeException("获取锁失败");
            }
            //再次检查缓存
            voucherStr = stringRedisTemplate.opsForValue().get(String.format(VOUCHER_KEY, id));
            if (StrUtil.isNotBlank(voucherStr)) {
                return JSON.parseObject(voucherStr, VoucherVO.class);
            }
            //查询数据库
            Voucher voucher = voucherMapper.selectById(id);
            if (voucher == null) {
                //数据库中确实无数据，缓存空值
                stringRedisTemplate.opsForValue().set(String.format(NULL_VOUCHER_KEY, id),
                        "-",
                        30,
                        TimeUnit.MINUTES);
                return null;
            }
            //不为空，缓存重建
            VoucherVO voucherVO = BeanUtil.copyProperties(voucher, VoucherVO.class);
            stringRedisTemplate.opsForValue().set(
                    String.format(VOUCHER_KEY, id),
                    JSON.toJSONString(voucherVO),
                    2,
                    TimeUnit.HOURS);
            return voucherVO;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 新增代金券
     */
    @Override
    public void save(VoucherSaveDTO requestParam) {
        //状态：0-未开始 1-进行中 2-已结束 3-已下架
        int status = VOUCHER_STATUS_NOT_STARTED;
        // 校验时间逻辑
        if (requestParam.getBeginTime().isAfter(requestParam.getEndTime())) {
            throw new BaseException("生效时间不能晚于失效时间");
        }
        if (requestParam.getPayValue() > requestParam.getActualValue()) {
            throw new BaseException("支付金额不能大于抵扣金额");
        }
        //判断如果当前时间处于生效时间和结束时间之间，状态设置为进行中
        LocalDateTime nowTime = LocalDateTime.now();
        if (nowTime.isBefore(requestParam.getEndTime()) && nowTime.isAfter(requestParam.getBeginTime())) {
            status = VOUCHER_STATUS_ACTIVE;
        } else if (nowTime.isAfter(requestParam.getEndTime())) {
            status = VOUCHER_STATUS_ENDED;
        }
        //设置创建人（获取当前登录用户用户名）
        Voucher voucher = BeanUtil.copyProperties(requestParam, Voucher.class);
        voucher.setStatus(status);
        voucher.setCreateUser(BaseContext.getCurrentId());
        voucher.setUpdateUser(BaseContext.getCurrentId());
        voucherMapper.insert(voucher);

        //缓存预热
        stringRedisTemplate.opsForValue().set(
                String.format(VOUCHER_KEY, voucher.getId()),
                JSON.toJSONString(BeanUtil.copyProperties(voucher, VoucherVO.class)),
                2,
                TimeUnit.HOURS);

        //删除热门代金券缓存
        stringRedisTemplate.delete(HOT_VOUCHER_LIST_KEY);
        createCachePenetrationBloomFilter.add(voucher.getId().toString());
    }

    /**
     * 更新代金券
     */
    @Override
    public void update(VoucherUpdateDTO requestParam) {
        int status = VOUCHER_STATUS_NOT_STARTED;
        //查询代金券对象
        LambdaQueryWrapper<Voucher> queryWrapper = Wrappers.lambdaQuery(Voucher.class)
                .eq(Voucher::getId, requestParam.getId());
        Voucher hasVoucher = voucherMapper.selectOne(queryWrapper);
        if (hasVoucher == null) {
            throw new BaseException("代金券不存在");
        }

        // 校验时间逻辑
        if (requestParam.getBeginTime().isAfter(requestParam.getEndTime())) {
            throw new BaseException("生效时间不能晚于失效时间");
        }
        if (requestParam.getPayValue() > requestParam.getActualValue()) {
            throw new BaseException("支付金额不能大于抵扣金额");
        }

        //获取分布式读写锁
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(String.format(VOUCHER_UPDATE_LOCK_KEY, requestParam.getId()));
        RLock rLock = readWriteLock.writeLock();
        if (!rLock.tryLock()) {
            throw new ServiceException("代金券正在被更新，请稍后再试...");
        }
        try {
            //判断如果当前时间处于生效时间和结束时间之间，状态设置为进行中
            LocalDateTime nowTime = LocalDateTime.now();
            if (nowTime.isBefore(requestParam.getEndTime()) && nowTime.isAfter(requestParam.getBeginTime())) {
                status = VOUCHER_STATUS_ACTIVE;
            } else if (nowTime.isAfter(requestParam.getEndTime())) {
                status = VOUCHER_STATUS_ENDED;
            }
            LambdaUpdateWrapper<Voucher> updateWrapper = Wrappers.lambdaUpdate(Voucher.class)
                    .eq(Voucher::getId, requestParam.getId());
            Voucher voucher = Voucher.builder()
                    .title(requestParam.getTitle())
                    .subTitle(requestParam.getSubTitle())
                    .rules(requestParam.getRules())
                    .payValue(requestParam.getPayValue())
                    .actualValue(requestParam.getActualValue())
                    .type(requestParam.getType())
                    .stock(requestParam.getStock())
                    .beginTime(requestParam.getBeginTime())
                    .endTime(requestParam.getEndTime())
                    .updateUser(BaseContext.getCurrentId())
                    .updateTime(LocalDateTime.now())
                    .build();
            voucher.setStatus(status);
            int updateCount = voucherMapper.update(voucher, updateWrapper);

            if (updateCount == 0) {
                throw new BaseException("代金券更新失败");
            }

            // 删除旧缓存
            stringRedisTemplate.delete(String.format(VOUCHER_KEY, requestParam.getId()));
            stringRedisTemplate.delete(HOT_VOUCHER_LIST_KEY);
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 根据id删除代金券
     */
    @Override
    public void deleteById(Long id) {
        //检查代金券是否存在
        Voucher voucher = voucherMapper.selectById(id);
        if (voucher == null) {
            throw new BaseException("代金券不存在");
        }
        // 删除所有相关缓存
        Set<String> keysToDelete = new HashSet<>();
        keysToDelete.add(String.format(VOUCHER_KEY, id));
        keysToDelete.add(String.format(NULL_VOUCHER_KEY, id));
        keysToDelete.add(HOT_VOUCHER_LIST_KEY);
        stringRedisTemplate.delete(keysToDelete);
        //删除数据库数据
        voucherMapper.deleteById(id);
    }

    /**
     * 修改代金券状态
     */
    @Override
    public void updateStatus(VoucherUpdateStatusDTO requestParam) {
        Long id = requestParam.getId();
        Integer isOnline = requestParam.getIsOnline();
        Voucher existVoucher = voucherMapper.selectById(id);
        if (existVoucher == null) {
            throw new BaseException("代金券不存在");
        }

        Voucher Voucher = new Voucher();
        Voucher.setId(id);
        Voucher.setIsOnline(isOnline);
        voucherMapper.updateById(Voucher);
        // 清理所有相关缓存
        Set<String> keysToDelete = new HashSet<>();
        keysToDelete.add(String.format(VOUCHER_KEY, id));
        keysToDelete.add(String.format(NULL_VOUCHER_KEY, id));
        keysToDelete.add(HOT_VOUCHER_LIST_KEY);
        stringRedisTemplate.delete(keysToDelete);
    }

    /**
     * 判断是否上架
     */
    @Override
    public Boolean isOnline(Long id) {
        Voucher voucher = baseMapper.selectById(id);
        if (voucher==null){
            throw new BaseException("优惠券信息不存在");
        }
        Integer isOnline = voucher.getIsOnline();
        return VOUCHER_ONLINE == isOnline;
    }

    /**
     * 查询已上架的优惠券
     */
    @Override
    public List<UserVoucherQueryVO> queryVoucher() {
        LambdaQueryWrapper<Voucher> queryWrapper = Wrappers.lambdaQuery(Voucher.class)
                .eq(Voucher::getIsOnline, VOUCHER_ONLINE);
        List<Voucher> voucherList = baseMapper.selectList(queryWrapper);
        return BeanUtil.copyToList(voucherList,UserVoucherQueryVO.class);
    }
}